﻿#include "httplib.h"
#include <iostream>
#include <sstream>  
#ifdef WIN32
#include<WinSock2.h>
#include <ws2tcpip.h>
#pragma comment(lib,"ws2_32.lib")
#endif

#ifdef __linux__
	
#endif // __linux__

namespace Network {
	namespace
	{
#define SPACE " "
#define ENTER "\r"
#define NEXTLINE "\n"
#define ENTERNEXT "\r\n"
#define DOUBLEENTERNECT "\r\n\r\n"
#define COLON ":"

		constexpr int httpsIndex = 8;
		constexpr int httpIndex = 7;
		std::map<Network::RequestMethoad, std::string> s_methoadMap =
		{
				{Network::RequestMethoad::GET, "GET"},
				{Network::RequestMethoad::POST, "POST"}
		};
	}


	HttpRequest::HttpRequest(const std::string& url, RequestMethoad methoad, const QueryParams& params)
	{
		init(url, methoad, params);
	}

	HttpRequest::~HttpRequest()
	{

	}

	// TODO: 添加日志
	void HttpRequest::run()
	{
		// 目前先仅支持http
#ifdef WIN32
		WSADATA wsaData;
		int result = WSAStartup(MAKEWORD(2, 2), &wsaData);
		if (result != 0)
			return;
		
		SOCKET sock = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
		if (sock == INVALID_SOCKET)
		{
			WSACleanup();
			return;
		}

		struct sockaddr_in server;
		server.sin_addr.s_addr = inet_addr(_requestSt._header._host.c_str());
		server.sin_family = AF_INET;
		//
		server.sin_port = htons(std::stoi(_requestSt._header._port));

		if (connect(sock, (struct sockaddr*)&server, sizeof(server)) < 0)
		{
			int error = WSAGetLastError();
			closesocket(sock);
			WSACleanup();
			return;
		}

		std::string reqStr = _requestSt.toString();
		const char* request = reqStr.c_str();

		if (send(sock, request, strlen(request), 0) < 0)
		{
			closesocket(sock);
			WSACleanup();
			return;
		}

		char response[4096];
		int bytes_recived = recv(sock, response, sizeof(response), 0);
		
		if (bytes_recived < 0) {
			printf("Error at recv(): %d\n", WSAGetLastError());
		}
		else {
			response[bytes_recived] = '\0';
			printf("Received: %s\n", response);
		}
		int error = WSAGetLastError();
		std::string responseStr = response;
		closesocket(sock);
		WSACleanup();
#endif // WIN32

	}

	void HttpRequest::init(const std::string& url,RequestMethoad methoad,const QueryParams& params)
	{
		_requestSt._header._requestMethoad = s_methoadMap[methoad];
		parseScheam(url, _requestSt);
		parseDomainAndPath(url,_requestSt);
		parseHost(_requestSt._header._domain, _requestSt);
		initDefaultHeaders(_requestSt);
		initBody(params, _requestSt);
	}

	void HttpRequest::parseScheam(const std::string& url, RequestSRT& reqSt)
	{
		std::string tokenHttps = "https://";
		std::string tokenHttp = "http://";
		// 匹配 https://或者http://
		std::string subStr1 = url.substr(0, 7);
		std::string subStr2 = url.substr(0, 8);

		if (subStr1 == tokenHttp)
			reqSt._header._scheamType = ScheamType::Scheam_http;
		else if (subStr2 == tokenHttps)
			reqSt._header._scheamType = ScheamType::Scheam_https;
	}

	void HttpRequest::parseDomainAndPath(const std::string& url, RequestSRT& reqSt)
	{
		// https:://wwww.xxx.com/
		int startDomainIndex = 0;
		switch (reqSt._header._scheamType)
		{
		case HttpRequest::ScheamType::Scheam_http:
			startDomainIndex = httpIndex;
			break;
		case HttpRequest::ScheamType::Scheam_https:
			startDomainIndex = httpsIndex;
			break;
		default:
			break;
		}

		std::string domain;
		size_t size = url.size();
		while (startDomainIndex < size && url[startDomainIndex] != '/')
		{
			domain += url[startDomainIndex++];
		}
		reqSt._header._domain = domain;

		// after domain
		reqSt._header._path = url.substr(startDomainIndex, size);
	}

	// ignore special domain with port
	// rex: www.red.com:123
	void HttpRequest::parseHost(const std::string domain, RequestSRT& reqSt)
	{
#ifdef WIN32
		struct addrinfo hints, * result, * p;

		WSADATA wsaData;
		if (WSAStartup(MAKEWORD(2, 2), &wsaData) != 0)
		{
			return;
		}

		std::memset(&hints, 0, sizeof hints);
		hints.ai_family = AF_UNSPEC;
		hints.ai_socktype = SOCK_STREAM;

		int status = getaddrinfo(domain.c_str(), nullptr, &hints, &result);
		if (status != 0)
		{
			WSACleanup();
		}
		// 此处获取的ip地址为一个列表，目前先取第一个
		for (p = result; p != nullptr; p = p->ai_next)
		{
			char ip[INET6_ADDRSTRLEN];
			void* addr;


			if (p->ai_family == AF_INET)
			{
				struct sockaddr_in* ipv4 = (struct sockaddr_in*)p->ai_addr;
				addr = &(ipv4->sin_addr);
			}
			else
			{
				struct sockaddr_in6* ipv6 = (struct sockaddr_in6*)p->ai_addr;
				addr = &(ipv6->sin6_addr);
			}

			inet_ntop(p->ai_family, addr, ip, sizeof ip);
			reqSt._header._host = ip;
			if (!reqSt._header._host.empty())
				break;
		}
		freeaddrinfo(result);
		WSACleanup();
#endif // WIN32

	}

	void HttpRequest::initDefaultHeaders(RequestSRT& reqSt)
	{
		reqSt._header._defaultHead = {
			{"Accept", "text/html,application/xhtml+xml,application/xml"},
			{"Accept-Charset","utf-8"},
			{"Content-Type","application/json"},
			{"Accept-Language","zh-CN,en-US"},
			{"Connection","keep-alive"},
			{"Host",reqSt._header._host + ":" + reqSt._header._port},
			{"User-Agent", "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/124.0.0.0 Safari/537.36 Edg/124.0.0.0"}
		};
	}

	// convert params to json
	void HttpRequest::initBody(const QueryParams& params, RequestSRT& reqSt)
	{
		// how to convert
		// using jsoncpp?==>json
		// just moc;


		std::string jsonBoby = "{\"loginName\":\"RedMudStove\",\"loginPwd\":\"5486426\"}";
		reqSt._body = jsonBoby;
	}

	// TODO: get can use boby?
	std::string HttpRequest::RequestSRT::toString()
	{
		std::string request;
		// request line
		request.append(_header._requestMethoad + SPACE + _header._path + SPACE + _header._httpVersion + ENTER + NEXTLINE);

		// default head;
		for (auto iter : _header._defaultHead)
			request.append(iter.first + COLON + iter.second + ENTERNEXT);

		// userHeader;

		for (auto iter : _header._queryHead)
			request.append(iter.first + COLON + iter.second + ENTERNEXT);

		request.append(std::string("Content-Length") + COLON + std::to_string(_body.length()) + ENTERNEXT);
		

		if (_header._requestMethoad == s_methoadMap[Network::RequestMethoad::GET])
		{
			request.append(ENTERNEXT);
		}
		else if (_header._requestMethoad == s_methoadMap[Network::RequestMethoad::POST])
		{
			request.append(ENTERNEXT);
			request.append(_body);
			request.append(ENTERNEXT);
		}

		

		return std::move(request);
	}

	void Network::request(const std::string url, RequestMethoad methoad, const QueryParams& params, NetWorkSuccess success, NetWorkError error)
	{
		HttpRequest* pRequest = new HttpRequest(url, methoad,params);
		pRequest->run();
	}
}